package com.wsoft.aspect;

import com.wsoft.constant.SensitiveConstant;
import com.wsoft.response.ResponseResult;
import com.wsoft.utils.SensitiveDataUtil;
import com.wsoft.utils.Sm4EncryptionUtil;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.List;

/**
 * @Author Zhang gukai
 * @Date 2024/11/12 9:14
 */
@Aspect
@Component
public class EncryptAspect {
    @Autowired
    private Sm4EncryptionUtil sm4EncryptionUtil;
    @Autowired
    private SensitiveDataUtil sensitiveDataUtil;
    @Resource
    private HttpServletRequest request;

    private static final List<String> PATH_LIST = Arrays.asList("/v3/api-docs","/download","/downloadPart");

    @Around("@annotation(org.springframework.web.bind.annotation.PostMapping) || @annotation(org.springframework.web.bind.annotation.GetMapping)")
    public Object encryptFields(ProceedingJoinPoint joinPoint) throws Throwable {
        Object[] args = joinPoint.getArgs();
        for (Object arg : args) {
            if (arg != null) {
                encryptObjectFields(arg);
            }
        }
        String url = request.getRequestURL().toString();
        for (String urlStr : PATH_LIST) {
            if(url.indexOf(urlStr) > 0){
                return joinPoint.proceed(args);
            }
        }

        //如果不是ResponseResult，跳过
        Object object = joinPoint.proceed(args);
        if(!(object  instanceof  ResponseResult)){
            return object;
        }

        ResponseResult result = (ResponseResult) object;
        if (result != null&&result.getData()!=null) {
            decryptAndMaskObjectFields(result.getData());
        }
        return result;
    }

    private void encryptObjectFields(Object obj) throws IllegalAccessException {
        for (Field field : obj.getClass().getDeclaredFields()) {
            if (field.isAnnotationPresent(EncryptField.class)) {
                field.setAccessible(true);
                Object value = field.get(obj);
                if (value != null) {
                    try {
                        String encryptedValue = sm4EncryptionUtil.encrypt(value.toString());
                        field.set(obj, encryptedValue);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    private void decryptAndMaskObjectFields(Object obj) throws IllegalAccessException {
        for (Field field : obj.getClass().getDeclaredFields()) {
            if (field.isAnnotationPresent(DecryptField.class)) {
                field.setAccessible(true);
                Object value = field.get(obj);
                if (value != null) {
                    try {
                        String decryptedValue = sm4EncryptionUtil.decrypt(value.toString());
                        field.set(obj, decryptedValue);
                        if (field.isAnnotationPresent(SensitiveField.class)){
                                String maskedValue = maskSensitiveData(decryptedValue.toString());
                                field.set(obj, maskedValue);
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
            else if (field.isAnnotationPresent(SensitiveField.class)) {
                field.setAccessible(true);
                Object value = field.get(obj);
                if (value != null) {
                    String maskedValue = maskSensitiveData(value.toString());
                    field.set(obj, maskedValue);
                }
            }
        }
    }

    private String maskSensitiveData(String value) {
        if (value.matches(SensitiveConstant.PHONE)) {
            return sensitiveDataUtil.maskPhoneNumber(value);
        } else if (value.matches(SensitiveConstant.ID_CARD)) {
            return sensitiveDataUtil.maskIdCard(value);
        } else if (value.contains(SensitiveConstant.EMAIL)) {
            return sensitiveDataUtil.maskEmail(value);
        } else {
            return sensitiveDataUtil.maskName(value);
        }
    }
}